/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package classcreator;

import classcreator.DB.DBTables;
import classcreator.DB.PText;
import classcreator.abstractclasses.AbstractClass;
import classcreator.abstractclasses.AbstractFK;
import classcreator.abstractclasses.AbstractProperty;
import classcreator.abstractclasses.EnumDataType;
import classcreator.classfillers.ABMConnection;
import classcreator.classfillers.ABMMeth;
import classcreator.classfillers.ABMSent;
import classcreator.classfillers.InitialData;
import classcreator.classfillers.Sorters;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Samus
 */
public class ClassCreator {

    /**
     * @param args the command line arguments
     */
    private static ClassCreator instance = new ClassCreator();
    private List<AbstractClass> clasesAbs = new ArrayList<AbstractClass>();
    private AbstractClass cache = null;
    private String packagename = null;

    private ClassCreator() {
    }

    public static ClassCreator getInstance() {
        return instance;
    }

    public List<AbstractClass> getClasesAbs() {
        return clasesAbs;
    }

    public AbstractClass getCache() {
        return cache;
    }

    public void setCache(AbstractClass cache) {
        this.cache = cache;
    }

    public String getPackagename() {
        return packagename;
    }

    public void setPackagename(String packagename) {
        this.packagename = packagename;
    }

    private void assignCache(String classname) {
        for (AbstractClass c : this.getClasesAbs()) {
            if (c.getNombreclase().equals(classname)) {
                setCache(c);
            }
        }
    }

    public void addAbsClass(String classname, String inheritance, boolean haspk) {
        AbstractClass clase = new AbstractClass();
        clase.setNombreclase(classname);
        clase.setAutoPk(haspk);
        clase.setInheritance(inheritance);
        this.clasesAbs.add(clase);
        PKCleaner(clase);
    }

    public boolean addAbsProp(String classname, String propname, boolean pk, String type, String collection) {
        boolean res = false;
        if (cache != null && cache.getNombreclase().equals(classname)) {
            res = cache.addProperty(Sorters.getInstance().reversePropSort(type), propname, pk, collection);
        } else {
            assignCache(classname);
            res = cache.addProperty(Sorters.getInstance().reversePropSort(type), propname, pk, collection);
        }
        return res;
    }

    public void addAbsFK(String classname, String propname, String fk, String refclass, EnumDataType type) {
        assignCache(classname);
        this.cache.addFK(type, propname, refclass, fk);
    }

    public void delAbsClass(String classname) {
        int i = 0;
        while (i < getClasesAbs().size()) {
            if (getClasesAbs().get(i).getNombreclase().equals(classname)) {
                this.clasesAbs.remove(i);
            }
            i++;
        }
    }

    public void clearClasses() {
        this.clasesAbs.clear();
    }

    public void clearPropsClass(String classname) {
        searchClass(classname).clearProps();
    }

    public void clearFksClass(String classname) {
        searchClass(classname).clearFKs();
    }

    public void delAbsProp(String classname, String propname) {
        assignCache(classname);
        cache.deleteProp(propname);
    }

    public void delAbsFK(String classname, String fkname) {
        assignCache(classname);
        cache.deleteFK(fkname);
    }

    public void assembleClasses() {
        ArrayList<AbstractClass> classaux = new ArrayList<AbstractClass>();
        classaux = (ArrayList<AbstractClass>) getClasesAbs();
        for (int i = 0; i < classaux.size(); i++) {
            assembleClass(packagename, classaux.get(i));
        }
        for (int i = 0; i < classaux.size(); i++) {
            inheritanceChecker(classaux.get(i));
        }
        for (int i = 0; i < classaux.size(); i++) {
            assembleClassEnd(classaux.get(i));
        }
        createControllerClass();
        createMainClass();
        createConnectionClass();
    }

    private void assembleClass(String packagename, AbstractClass clase) {
        collectionChecker(clase);
        collectionCleaner(clase);
        InitialData.getInstance().addInitData(packagename, clase);
    }

    private boolean inheritanceChecker(AbstractClass c) {
        boolean res = false;
        if (!c.getInheritance().equals("")) {
            AbstractClass classaux = searchClass(c.getInheritance());
            if (classaux != null) {
                for (AbstractProperty p : classaux.readPKs()) {
                    c.getProperties().add(p);
                    c.addFK(p.getType(), p.getName(), classaux.getNombreclase(), p.getName());
                }
            }
        }
        return res;
    }

    private void assembleClassEnd(AbstractClass clase) {
        classcreator.classfillers.Properties.getInstance().assembleProperties(clase);
        clase.setAssembledClass(InitialData.getInstance().closeClass(clase.getAssembledClass()));
        ABMSent.getInstance().createSentences(clase);
        ABMMeth.getInstance().addMethods(clase);
    }

    private boolean collectionChecker(AbstractClass c) {
        boolean res = false;
        int i = 0;
        while (i < c.getProperties().size()) {
            AbstractProperty p = c.getProperties().get(i);
            if (p.getType() == EnumDataType.COLLECTION) {
                AbstractFK fk = c.searchFK(p.getName());
                AbstractClass clase = searchClass(p.getCollection());
                if (clase == null) {
                    if (fk == null) {
                        addAbsClass("Collection" + p.getName(), "", true);
                        for (AbstractProperty pk : c.readPKs()) {
                            addAbsProp("Collection" + p.getName(), "fk" + pk.getName(), false, p.getCollection(), "");
                        }
                    }
                } else {
                    if (fk == null) {
                        addAbsClass("Collection" + p.getName(), "", true);
                        for (AbstractProperty pk : clase.readPKs()) {
                            addAbsProp("Collection" + p.getName(), "fk" + pk.getName(), false, "String", "");
                            addAbsFK("Collection" + p.getName(), "fk" + pk.getName(), pk.getName(), clase.getNombreclase(), EnumDataType.STRING);
                        }
                    }
                }
            }
            i++;
        }
        return res;
    }

    public AbstractClass PKCleaner(AbstractClass clase) {
        if (clase.isAutoPk() == true) {
            addAbsProp(clase.getNombreclase(), "autogenid", true, "Int", "");
            clase.setAutoPk(true);
        }
        return clase;
    }

    public void collectionCleaner(AbstractClass c) {
        int size = c.getProperties().size();
        int i = 0;
        while (i < size) {
            AbstractProperty p = c.getProperties().get(i);
            if (p.getType() == EnumDataType.COLLECTION) {
                c.deleteProp(p.getName());
                size--;
            } else {
                i++;
            }
        }
    }

    public AbstractClass searchClass(String classname) {
        AbstractClass clase = null;
        for (AbstractClass c : getClasesAbs()) {
            if (c.getNombreclase().equals(classname)) {
                clase = c;
            }
        }
        return clase;
    }

    public void createControllerClass() {
        AbstractClass c = new AbstractClass();
        c.setNombreclase("Controller");
        c.setInheritance("");
        c.setAutoPk(false);
        InitialData.getInstance().addInitData(packagename, c);
        ABMMeth.getInstance().addSingleton(c);
        for (AbstractClass cla : getClasesAbs()) {
            for (String s : cla.getAssembledMethods()) {
                c.getAssembledClass().add(s);
            }
        }
        InitialData.getInstance().closeClass(c.getAssembledClass());
        getClasesAbs().add(c);
    }

    public void createConnectionClass() {
        AbstractClass c = new AbstractClass();
        c.setNombreclase("DataBaseConnFactory");
        c.setInheritance("");
        c.setAutoPk(false);
        InitialData.getInstance().addInitData(packagename, c);
        ABMConnection.getInstance().addProps(c.getAssembledClass());
        ABMMeth.getInstance().addSingleton(c);
        ABMConnection.getInstance().addDriverSupport(c.getAssembledClass());
        ABMConnection.getInstance().addConnectionGiver(c.getAssembledClass());
        InitialData.getInstance().closeClass(c.getAssembledClass());
        getClasesAbs().add(c);
    }

    public void createMainClass() {
        AbstractClass c = new AbstractClass();
        c.setNombreclase("Main");
        c.setInheritance("");
        c.setAutoPk(false);
        InitialData.getInstance().addInitData(packagename, c);
        ABMMeth.getInstance().addMainMeth(c);
        InitialData.getInstance().closeClass(c.getAssembledClass());
        getClasesAbs().add(c);
    }

    public void saveClasses() throws IOException {
        for (AbstractClass c : this.getClasesAbs()) {
            PText.getInstance().persistirMensaje(c.getAssembledClass(), c.getNombreclase());
        }
    }

    public void executeAll() throws SQLException, IOException {
        saveClasses();
        DBTables.getInstance().createDatabase();
        for (AbstractClass c : getClasesAbs()) {
            if (!c.getSQLSentenceCreate().isEmpty()) {
                DBTables.getInstance().executeQuery(c.getSQLSentenceCreate());
            }
        }
        for (AbstractClass c : getClasesAbs()) {
            if (!c.getSQLSentenceFK().isEmpty()) {
                DBTables.getInstance().executeQuery(c.getSQLSentenceFK());
            }
        }
        copyLibrary();
    }

    public void copyLibrary() {
        try {
            PText.getInstance().copyLibrary();
        } catch (IOException ex) {
            Logger.getLogger(ClassCreator.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
